﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LoungeRepo.Contracts.Core;
using LoungeRepo.Contracts.Normalizer;
using LoungeRepo.Normalizer;
using LoungeRepo.Normalizer.DataStructures;
using NUnit.Framework;

namespace Test.LoungeRepo.Normalizer
{
    [TestFixture]
    public class testNormalizer
    {
        private class StringEntityWithTransientField : ILoungeRepoEntityIdentity
        {
            #region Implementation of ILoungeRepoEntityIdentity

            public string Id
            {
                get { return "myid"; }
            }

            public string Partition
            {
                get { return "mypartition"; }
            }

            #endregion

            public string Text;

            [NonSerialized] public string Comment;
        }

        private class ParentEntity : ILoungeRepoEntityIdentity
        {
            public class ChildObject
            {
                public string Text;
                public ChildObject Child;
            }

            #region Implementation of ILoungeRepoEntityIdentity

            public string Id {get; set;}
            public string Partition {get; set;}

            #endregion

            public string Text;

            public ChildObject Child;
        }

        private class NodeEntity : ILoungeRepoEntityIdentity
        {
            #region Implementation of ILoungeRepoEntityIdentity

            public string Id { get; set; }
            public string Partition { get; set; }

            #endregion

            public string Text;
            public NodeEntity Child;
        }


        [Test]
        public void Normalize_entity()
        {
            var sut = new EntityNormalizer(null);

            var ne = sut.Normalize(new StringEntity { Text = "hello, world" })
                            .First();

            Assert.AreEqual(ne.Id, "myid");
            Assert.AreEqual(ne.Partition, "mypartition");
            Assert.IsTrue(ne.ObjectType == typeof(StringEntity));

            Assert.AreEqual(1, ne.Fields.Count());

            Assert.AreEqual("Text", ne.Fields.ElementAt(0).Name);
            Assert.AreEqual("hello, world", (string)ne.Fields.ElementAt(0).Value);
        }


        [Test]
        public void Normalize_nonEntity()
        {
            var sut = new EntityNormalizer(null);

            var ne = sut.Normalize("hello, world", "myid", "mypartition")
                            .First();

            Assert.AreEqual(ne.Id, "myid");
            Assert.AreEqual(ne.Partition, "mypartition");
            Assert.IsTrue(ne.ObjectType == typeof(WrapperEntity));

            Assert.AreEqual(3, ne.Fields.Count());

            Assert.IsTrue(ne.Fields.ElementAt(0).Name.StartsWith("<Id>"));
            Assert.AreEqual("myid", (string)ne.Fields.ElementAt(0).Value);

            Assert.IsTrue(ne.Fields.ElementAt(1).Name.StartsWith("<Partition>"));
            Assert.AreEqual("mypartition", (string)ne.Fields.ElementAt(1).Value);

            Assert.IsTrue(ne.Fields.ElementAt(2).Name.StartsWith("<NonEntityObject>"));
            Assert.AreEqual("hello, world", (string)ne.Fields.ElementAt(2).Value);
        }


        [Test]
        public void Normalize_nested_entities()
        {
            EntityNode gcnode = new EntityNode {Id = "gc", Partition = ""};
            EntityNode cnode = new EntityNode { Id="c", Partition="", Next=gcnode};
            EntityNode pnode = new EntityNode {Id="p", Partition="", Next=cnode};

            var sut = new EntityNormalizer(null);
            var ne = sut.Normalize(pnode);

            Assert.AreEqual(3, ne.Count());
            Assert.AreEqual("p", ne.First().Id);
            Assert.AreEqual("c", ne.ElementAt(1).Id);
            Assert.AreEqual("gc", ne.ElementAt(2).Id);

            var next = (ILoungeRepoEntityIdentity)ne.First().Fields.Single(f => f.Name == "Next").Value;
            Assert.IsFalse(next is INormalizedEntity);
            Assert.AreEqual(cnode.Id, next.Id);
        }


        [Test]
        public void Normalize_nested_cyclic_entities()
        {
            EntityNode pnode = new EntityNode { Id = "p", Partition = "" };
            pnode.Next = pnode;

            var sut = new EntityNormalizer(null);
            var ne = sut.Normalize(pnode);

            Assert.AreEqual(1, ne.Count());
            Assert.AreEqual("p", ne.First().Id);

            var next = (ILoungeRepoEntityIdentity)ne.First().Fields.Single(f => f.Name == "Next").Value;
            Assert.IsInstanceOfType(typeof(NormalizedEntityReference), next);
            Assert.AreEqual(pnode.Id, next.Id);
        }


        [Test]
        public void Denormalize_entity()
        {
            StringEntity se = new StringEntity {Text = "hello, world"};
            var sut = new EntityNormalizer(null);
            var ne = sut.Normalize(se).First();

            StringEntity se2 = (StringEntity)sut.Denormalize(ne);

            Assert.AreEqual(se.Text, se2.Text);
        }

        [Test]
        public void Denormalize_non_entity()
        {
            var sut = new EntityNormalizer(null);
            var ne = sut.Normalize("hello, world", "1", "p");

            var msg = sut.Denormalize(ne.First());
            Assert.IsInstanceOfType(typeof (string), msg);
            Assert.AreEqual("hello, world", msg);
        }


        [Test]
        public void Denormalize_nested_entities()
        {
            NodeEntity nChild = new NodeEntity { Id="c", Partition="p", Text = "c" };
            NodeEntity nParent = new NodeEntity { Id="p", Partition="q", Text = "p", Child = nChild };

            IEnumerable<INormalizedEntity> normalizedEntities = new INormalizedEntity[] {};
            var sut = new EntityNormalizer(eid =>
                                               {
                                                   var ne = normalizedEntities.Single(o => o.Id==eid.Id && o.Partition==eid.Partition);
                                                   return ne;

                                               });
            normalizedEntities = sut.Normalize(nParent);

            NodeEntity nParent2 = (NodeEntity)sut.Denormalize(normalizedEntities.First());

            Assert.AreEqual(nParent.Text, nParent2.Text);
            Assert.AreEqual(nChild.Text, nParent2.Child.Text);
        }


        [Test]
        public void Denormalize_cyclic_nested_entities()
        {
            NodeEntity nParent = new NodeEntity { Id = "myid", Partition = "mypart", Text = "hello" };
            nParent.Child = nParent;

            IEnumerable<INormalizedEntity> normalizedEntities = new INormalizedEntity[] { };
            var sut = new EntityNormalizer(eid =>
                                                {
                                                    var ne = normalizedEntities.Single(o => o.Id == eid.Id && o.Partition == eid.Partition);
                                                    return ne;

                                                });
            normalizedEntities = sut.Normalize(nParent);

            NodeEntity nParent2 = (NodeEntity)sut.Denormalize(normalizedEntities.First());

            Assert.AreSame(nParent2, nParent2.Child);
        }
    }
}
